package cn.com.guage.transtation;

import java.sql.*;
import java.util.UUID;

public class TccTransactionExample {

    // 数据库配置
    static class DBConfig {
        String url;
        String user;
        String password;

        DBConfig(String url, String user, String password) {
            this.url = url;
            this.user = user;
            this.password = password;
        }
    }

    static final DBConfig db1Config = new DBConfig(
            "jdbc:mysql://localhost:3306/tcc-db1", "root", "123456");

    static final DBConfig db2Config = new DBConfig(
            "jdbc:mysql://localhost:3306/tcc-db2", "root", "123456");

    public static void main(String[] args) {
        String xid = UUID.randomUUID().toString();
        long userId = 10001L;
        long productId = 1001L;
        int quantity = 8;

        try {
            // ========== Try阶段 ==========
            boolean tryResult = tryPhase(xid, userId, productId, quantity);

            if (tryResult) {
                // ========== Confirm阶段 ==========
                confirmPhase(xid);
                System.out.println("事务提交成功");
            } else {
                // ========== Cancel阶段 ==========
                cancelPhase(xid);
                System.out.println("事务已回滚");
            }
        } catch (SQLException e) {
            e.printStackTrace();
            cancelPhase(xid);
        }
    }

    // ==================== Try阶段 ====================
    private static boolean tryPhase(String xid, long userId,
                                    long productId, int quantity) throws SQLException {
        Connection orderConn = null;
        Connection inventoryConn = null;

        try {
            // 订单服务Try
            orderConn = getConnection(db1Config);
            if (!orderTry(orderConn, xid, userId, productId, quantity)) {
                return false;
            }

            // 库存服务Try
            inventoryConn = getConnection(db2Config);
            if (!inventoryTry(inventoryConn, xid, productId, quantity)) {
                return false;
            }

            return true;
        } finally {
            closeConnection(orderConn);
            closeConnection(inventoryConn);
        }
    }

    private static boolean orderTry(Connection conn, String xid,
                                    long userId, long productId, int quantity) throws SQLException {
        conn.setAutoCommit(false);

        try {
            // 1. 创建预订单
            String orderId = UUID.randomUUID().toString();
            PreparedStatement ps = conn.prepareStatement(
                    "INSERT INTO `order` (order_id, user_id, product_id, quantity, status) " +
                            "VALUES (?, ?, ?, ?, 'TRY')");
            ps.setString(1, orderId);
            ps.setLong(2, userId);
            ps.setLong(3, productId);
            ps.setInt(4, quantity);
            ps.executeUpdate();

            // 2. 记录订单TCC日志
            ps = conn.prepareStatement(
                    "INSERT INTO order_tcc_log (xid, order_id, status) VALUES (?, ?, 'TRY')");
            ps.setString(1, xid);
            ps.setString(2, orderId);
            ps.executeUpdate();

            conn.commit();
            return true;
        } catch (SQLException e) {
            conn.rollback();
            return false;
        }
    }

    private static boolean inventoryTry(Connection conn, String xid,
                                        long productId, int quantity) throws SQLException {
        conn.setAutoCommit(false);

        try {
            // 1. 检查并冻结库存
            PreparedStatement ps = conn.prepareStatement(
                    "UPDATE inventory SET frozen = frozen + ? " +
                            "WHERE product_id = ? AND total - frozen >= ?");
            ps.setInt(1, quantity);
            ps.setLong(2, productId);
            ps.setInt(3, quantity);
            int updated = ps.executeUpdate();

            if (updated == 0) {
                throw new SQLException("库存不足");
            }

            // 2. 记录库存TCC日志
            ps = conn.prepareStatement(
                    "INSERT INTO inventory_tcc_log (xid, product_id, quantity, status) " +
                            "VALUES (?, ?, ?, 'TRY')");
            ps.setString(1, xid);
            ps.setLong(2, productId);
            ps.setInt(3, quantity);
            ps.executeUpdate();

            conn.commit();
            return true;
        } catch (SQLException e) {
            conn.rollback();
            return false;
        }
    }

    // ==================== Confirm阶段 ====================
    private static void confirmPhase(String xid) throws SQLException {
        Connection orderConn = null;
        Connection inventoryConn = null;

        try {
            // 订单服务Confirm
            orderConn = getConnection(db1Config);
            orderConfirm(orderConn, xid);

            // 库存服务Confirm
            inventoryConn = getConnection(db2Config);
            inventoryConfirm(inventoryConn, xid);
        } finally {
            closeConnection(orderConn);
            closeConnection(inventoryConn);
        }
    }

    private static void orderConfirm(Connection conn, String xid) throws SQLException {
        conn.setAutoCommit(false);

        try {
            // 1. 更新订单状态
            PreparedStatement ps = conn.prepareStatement(
                    "UPDATE `order` o JOIN order_tcc_log l ON o.order_id = l.order_id " +
                            "SET o.status = 'CONFIRMED' WHERE l.xid = ?");
            ps.setString(1, xid);
            ps.executeUpdate();

            // 2. 更新TCC日志状态
            ps = conn.prepareStatement(
                    "UPDATE order_tcc_log SET status = 'CONFIRM' WHERE xid = ?");
            ps.setString(1, xid);
            ps.executeUpdate();

            conn.commit();
        } catch (SQLException e) {
            conn.rollback();
            throw e;
        }
    }

    private static void inventoryConfirm(Connection conn, String xid) throws SQLException {
        conn.setAutoCommit(false);

        try {
            // 1. 扣减真实库存
            PreparedStatement ps = conn.prepareStatement(
                    "UPDATE inventory i JOIN inventory_tcc_log l ON i.product_id = l.product_id " +
                            "SET i.total = i.total - l.quantity, i.frozen = i.frozen - l.quantity " +
                            "WHERE l.xid = ?");
            ps.setString(1, xid);
            ps.executeUpdate();

            // 2. 更新TCC日志状态
            ps = conn.prepareStatement(
                    "UPDATE inventory_tcc_log SET status = 'CONFIRM' WHERE xid = ?");
            ps.setString(1, xid);
            ps.executeUpdate();

            conn.commit();
        } catch (SQLException e) {
            conn.rollback();
            throw e;
        }
    }

    // ==================== Cancel阶段 ====================
    private static void cancelPhase(String xid) {
        try {
            // 订单服务Cancel
            try (Connection conn = getConnection(db1Config)) {
                orderCancel(conn, xid);
            }

            // 库存服务Cancel
            try (Connection conn = getConnection(db2Config)) {
                inventoryCancel(conn, xid);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    private static void orderCancel(Connection conn, String xid) throws SQLException {
        conn.setAutoCommit(false);

        try {
            // 1. 删除预订单
            PreparedStatement ps = conn.prepareStatement(
                    "DELETE o FROM `order` o JOIN order_tcc_log l ON o.order_id = l.order_id " +
                            "WHERE l.xid = ?");
            ps.setString(1, xid);
            ps.executeUpdate();

            // 2. 更新TCC日志状态
            ps = conn.prepareStatement(
                    "UPDATE order_tcc_log SET status = 'CANCEL' WHERE xid = ?");
            ps.setString(1, xid);
            ps.executeUpdate();

            conn.commit();
        } catch (SQLException e) {
            conn.rollback();
            throw e;
        }
    }

    private static void inventoryCancel(Connection conn, String xid) throws SQLException {
        conn.setAutoCommit(false);

        try {
            // 1. 释放冻结库存
            PreparedStatement ps = conn.prepareStatement(
                    "UPDATE inventory i JOIN inventory_tcc_log l ON i.product_id = l.product_id " +
                            "SET i.frozen = i.frozen - l.quantity WHERE l.xid = ?");
            ps.setString(1, xid);
            ps.executeUpdate();

            // 2. 更新TCC日志状态
            ps = conn.prepareStatement(
                    "UPDATE inventory_tcc_log SET status = 'CANCEL' WHERE xid = ?");
            ps.setString(1, xid);
            ps.executeUpdate();

            conn.commit();
        } catch (SQLException e) {
            conn.rollback();
            throw e;
        }
    }

    // ==================== 工具方法 ====================
    private static Connection getConnection(DBConfig config) throws SQLException {
        return DriverManager.getConnection(config.url, config.user, config.password);
    }

    private static void closeConnection(Connection conn) {
        try {
            if (conn != null && !conn.isClosed()) {
                conn.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}